/******************************************************************************/
/* File: udpTrafficSender.cpp                                                 */
/******************************************************************************/

#include "udpTrafficSender.h"

UDPTrafficSender::UDPTrafficSender(UDPConnectedSocket *pus, ThreadConf *ptc)
                : pUdpSock(pus), pThreadConf(ptc), iSendRate(0) {
    pSendBuf = new char[DataStore::generalSetting.iUDPBufferLength];
    UtilClass::PatternBuffer(pSendBuf, DataStore::generalSetting.iUDPBufferLength);           
}

UDPTrafficSender::~UDPTrafficSender() {
    if(pSendBuf) {
        delete pSendBuf;
        pSendBuf = NULL;
    }
}

int UDPTrafficSender::SendPacket(int iPacketID, int length) {
    struct timeval beforeTime;
    
    gettimeofday(&beforeTime, NULL);
    
    uint32_t packetSec, packetUSec, packetID;
    
    packetSec = htonl(beforeTime.tv_sec);
    packetUSec = htonl(beforeTime.tv_usec);
    packetID = htonl(iPacketID + 1);
    
    memcpy(pSendBuf, &packetSec, sizeof(packetSec));
    memcpy(pSendBuf + 4, &packetUSec, sizeof(packetUSec));
    memcpy(pSendBuf + 8, &packetID, sizeof(packetID));
    
    int iSendBytes = pUdpSock->Send(pSendBuf, length, 0);
    
    return iSendBytes;
}

void UDPTrafficSender::ExitCleanUp() {
    pThreadConf->SetStoppedFlag(1);
}

void UDPTrafficSender::Run() {
    
    std::cout << "Running udp traffic sender instance....\n";
    
    int updateInterval = DataStore::generalSetting.iUpdateInterval;
    int reportInterval = DataStore::generalSetting.iReportInterval;
    int numOnce = DataStore::generalSetting.iNumOnce;
    int length = DataStore::generalSetting.iUDPBufferLength;
    
    /*
    // 设置发送超时标志，以妨一直阻塞在send上
    struct timeval sendTimeout;
    sendTimeout.tv_sec = 0;
    sendTimeout.tv_usec = updateInterval;
    try {
        pUdpSock->SetSockOpt(SOL_SOCKET, SO_SNDTIMEO, (char *)&sendTimeout, sizeof(sendTimeout));
    } 
    catch (SocketException se) {
        se.PrintException();
        ExitCleanUp();
        return;
    }
    */
    
    double startTime = 0.0;
    double endTime = 0.0;
    double bandwidth = 0.0;
    int64_t totBytes = 0, curBytes = 0;
    int64_t packetCount = 0;
    int64_t packetID = 0;
    double diffUsecs = 0.0;
    struct timeval lastPacketTime, lastRepotTime, nowTime, reportTime, updateTime, timeoutTime;
    bool timeoutFlag = false;
    
    gettimeofday(&nowTime, NULL);
    lastPacketTime = nowTime;
    lastRepotTime = nowTime;
    reportTime = nowTime;
    UtilClass::TimevalAddLong(&reportTime, reportInterval);
    
    updateTime = nowTime;
    UtilClass::TimevalAddLong(&updateTime, updateInterval);
    
    timeoutTime = nowTime;
    if (pThreadConf->pFC->iTime > 0) {
        unsigned long iTOUsecs = pThreadConf->pFC->iTime * MILLION;
        UtilClass::TimevalAddLong(&timeoutTime, iTOUsecs);
        timeoutFlag = true;
    }
    
    iSendRate = pThreadConf->pFC->GetNextRate();
    pThreadConf->SetStartedFlag(1);
    
    while(true) {
        
        // 检查停止标志 --- 为了更好地解决，可以再做最后一次统计，然后再退出 
        if (pThreadConf->GetHaltFlag() == 1) {
            ExitCleanUp();
            return;
        }
            
        // 检查更新标志（先不考虑加锁操作） --- 更新速率
        if(pThreadConf->GetUpdateFlag() == 1)
            iSendRate = pThreadConf->pFC->GetNextRate();
        
        int count = 0;
        while(count < numOnce) {
            try {
                curBytes = SendPacket(packetCount, length);
                count++;
            }
            catch (SocketException se) {
                se.PrintException();
                std::cout << "UDP traffic sender shutdown for remote disconnected.\n";
                ExitCleanUp();
                return;
            }
            totBytes += curBytes;
            packetCount++;
            packetID++;
        }
        
        // 检查是否需要进行速率调整 --- 通过延迟nanosleep控制
        gettimeofday(&nowTime, NULL);
        diffUsecs = UtilClass::TimevalDiff(nowTime, lastPacketTime);
        bandwidth = CheckThrottleAndDelay(totBytes, diffUsecs);
        lastPacketTime = nowTime;
        
        // 检查是否需要上传统计报告
        gettimeofday(&nowTime, NULL);
        if(UtilClass::TimevalGreaterThan(nowTime, reportTime)) {
            diffUsecs = UtilClass::TimevalDiff(nowTime, lastRepotTime);
            std::cout << diffUsecs << std::endl;
            endTime += diffUsecs / MILLION;
            UploadReport(startTime, endTime, totBytes, bandwidth, packetCount);
            
            // 重置数据
            startTime = endTime;
            packetCount = 0;
            totBytes = 0;
                        
            // 重新设置reportTime
            reportTime = nowTime;
            lastRepotTime = nowTime;
            UtilClass::TimevalAddLong(&reportTime, reportInterval);
        }
        
        // 检查是否需要更新速率（getNextRate）
        gettimeofday(&nowTime, NULL);
        if(UtilClass::TimevalGreaterThan(nowTime, updateTime)) {
            iSendRate = pThreadConf->pFC->GetNextRate();
            // 重新设置updateTime
            updateTime = nowTime;
            UtilClass::TimevalAddLong(&updateTime, updateInterval);
        }
        
        // 检查是否到时（mTime参数）
        if (timeoutFlag) {
            if (UtilClass::TimevalGreaterThan(nowTime, timeoutTime)) {
                std::cout << "UDP traffic sender timeout and exit.\n";
                ExitCleanUp();
                return;
            }
        }
    }
}

double UDPTrafficSender::CheckThrottleAndDelay(int64_t iBytes, double fTimeUsecs) {
    double fExpectUsecs = 0 ;
    if (iSendRate != 0) 
        fExpectUsecs = MILLION * ((double)iBytes) / iSendRate;
    
    unsigned long iDelayUsecs = 0;        
    if (fExpectUsecs > fTimeUsecs) {
        iDelayUsecs = (unsigned long) (fExpectUsecs - fTimeUsecs);
        UtilClass::DelayLoop(iDelayUsecs);
        
        return (double) iSendRate;
    }
    
    double fBandwidth = MILLION * iBytes / fTimeUsecs;
    
    return fBandwidth;
}

void UDPTrafficSender::UploadReport(double begin, double end, int64_t transfer, double bandwidth, int64_t packets) {
    
    std::cout << "upload udp report...\n";
    
    UdpReport *pur = new UdpReport();
    pur->fBeginTime = begin;
    pur->fEndTime = end;
    pur->fInterval = end - begin;
    pur->iTransfer = transfer;
    pur->fBandwidth = bandwidth;
    pur->iPacketTotal = packets;
    
    // unused for udp sender.
    pur->fJitter = 0.0;
    pur->iPacketLost = 0;
    pur->fLoss = 0.0;
        
    Report *pr = new Report();
    pr->iRole = 1; //-- Sender
    pr->iProto = 2; // -- UDP

    char *lip = pUdpSock->LocalHostName();
    pr->sSenderIP = strdup(lip);
    pr->iSenderPort = pUdpSock->LocalPortNumber();
    char *rip = pUdpSock->RemoteHostName();
    pr->sRecverIP = strdup(rip);
    pr->iRecverPort = pUdpSock->RemotePortNumber();
    pr->pUdpReport = pur;

    Message *pm = new Message();
    pm->iMessageType = 3;
    pm->pReport = pr;
    
    pThreadConf->pMQ->PushMessage(pm);
}

/******************************************************************************/
/* End File: udpTrafficSender.cpp                                             */
/******************************************************************************/